'''
Created on Mar 4, 2014

@author: Benjamin Brewer
@license: GNU GPL v3
'''
from me.benbrewer.exercise.vending.coin import Quarter, Nickel, Dime
from decimal import Decimal, ROUND_DOWN, ROUND_UP, ROUND_05UP, ROUND_HALF_EVEN

class VendingMachine(object):
    '''
    classdocs
    '''
    def __init__(self):
        self.__display = "INSERT COIN"
        self.total = 0
        self.coinReturn = []
        
    def insertCoin(self, coin):
        # the point here is to get this to work,regardless of how bad the code is, so here is a super inelegant imp
        # we also assume that we have some sort of fancy mechanism for measuring mass, diameter and thickness
        # to determine value, we could really just hard set our value output, however, we'll be nice
        self.total = self.total + VendingMachine.validCoins.get((coin.mass, coin.diameter, coin.thickness), 0)
        self.display = "${0:.2f}".format(self.total)
        
        # bad, repetitive validity check
        if VendingMachine.validCoins.get((coin.mass, coin.diameter, coin.thickness), False) == False:
            self.coinReturn.append(coin)
            return False
        else:
            return True
        
    def pressButton(self, product):
        # super straightforward imp
        if self.total >= product.price:
            self.display = "THANK YOU"
            
            # extra requirement, always return largest coins first, if possible
            # TODO - all of this is a great candidate for a break out into function for a unit test, especially to see if HALF_EVEN works as desired
            if self.total > product.price:
                difference = Decimal(self.total - product.price).quantize(Decimal('.05'), rounding=ROUND_HALF_EVEN)
                for value in reversed(sorted(VendingMachine.coinMap.keys())):
                    while True:
                        possible = Decimal(Decimal(difference).quantize(Decimal('.05'), rounding=ROUND_HALF_EVEN) - Decimal(value).quantize(Decimal('.05'), rounding=ROUND_HALF_EVEN)).quantize(Decimal('.05'), rounding=ROUND_HALF_EVEN)  # candy test revealed some very imprecise float value returns
                        if possible < 0: break
                        difference = possible
                        self.coinReturn.append(VendingMachine.coinMap.get(value))
                        if difference == 0: break
                    # will do two extra iterations if largest coin can meet changing needs - will fix in later iterations
            
            return True
        else:
            # update the price display
            self.display = "PRICE ${0:.2f}".format(product.price)
            return False
    
    @property    
    def display(self):
        returned = self.__display
        if returned == "THANK YOU":
            self.__display = "INSERT COIN"
            self.total = 0
        if "PRICE" in returned:
            self.__display = "${0:.2f}".format(self.total) if self.total > 0 else "INSERT COIN"
        return returned
        
    @display.setter
    def display(self, value):
        self.__display = value

# we assume that our VendingMachine has its own standard for mass, diameter and thickness, regardless of what is literally defined for their classes
VendingMachine.validCoins = {(5, 21.21, 1.95):0.05, (2.26, 17.91, 1.35):0.10, (5.67, 24.26, 1.75):0.25}
VendingMachine.coinMap = {0.25:Quarter(), 0.10:Dime(), 0.05:Nickel()}  # dirty imp - plus, conceptually it's just returning the SAME coin

if __name__ == "__main__":
    pass
